﻿using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace TiaoTiaoCode.Common.Helpers
{
    /// <summary>
    /// XML序列化帮助类
    /// </summary>
    public class TiaoTiaoXmlSerializer
    {
        static readonly object locker = new object();

        /// <summary>
        /// 把T对象序列化后并保存成指定文件
        /// </summary>
        /// <param name="obj">T对象</param>
        /// <param name="fileName">文件路径</param>
        /// <typeparam name="T">泛型，必须为可序列化类</typeparam>
        public static void Serialize<T>(T obj, string fileName) where T : class
        {
            lock (locker)
            {
                string fullPath = fileName;
                using (FileStream fs = new FileStream(fullPath, FileMode.Create))
                {
                    SerializeInner(fs, obj, Encoding.UTF8);
                }
            }
        }

        /// <summary>
        /// XML反序列化
        /// </summary>
        /// <param name="fileName">需要反序列化的文件路径</param>
        /// <returns>反序列化后的T对象，如果文件不存在返回default(T)</returns>
        /// <typeparam name="T">泛型，必须为可序列化类</typeparam>
        public static T Deserialize<T>(string fileName) where T : class
        {
            lock (locker)
            {
                T obj;
                string fullPath = fileName;
                if (!File.Exists(fullPath))
                    return default!;

                using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read))
                {
                    XmlSerializer formatter = new XmlSerializer(typeof(T));
                    obj = (T)formatter.Deserialize(fs);
                }
                return obj;
            }
        }

        /// <summary>
        /// XML反序列化
        /// </summary>
        /// <param name="fileName">需要反序列化的文件路径</param>
        /// <param name="type">反序列化后的对象类型，必须为类</param>
        /// <returns>反序列化后的对象</returns>
        public static object? Deserialize(string fileName, Type type)
        {
            lock (locker)
            {
                object obj;
                string fullPath = fileName;
                if (!File.Exists(fullPath))
                    return null;

                using (FileStream fs = new FileStream(fullPath, FileMode.Open, FileAccess.Read))
                {
                    XmlSerializer formatter = new XmlSerializer(type);
                    obj = formatter.Deserialize(fs);
                }
                return obj;
            }
        }

        /// <summary>
        /// 把T对象序列化成xml字符串
        /// </summary>
        /// <param name="obj">T对象</param>
        /// <returns>xml字符串</returns>
        public static string SerializeTo<T>(T obj) where T : class
        {
            lock (locker)
            {
                using (StreamReader reader = new StreamReader(new MemoryStream(), Encoding.UTF8))
                {
                    Stream ms = reader.BaseStream;
                    SerializeInner(ms, obj, Encoding.UTF8);
                    ms.Position = 0;
                    return reader.ReadToEnd();
                }
            }
        }

        /// <summary>
        /// 把T对象序列化成xml字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">T对象</param>
        /// <param name="oneLine">是否单行输出</param>
        /// <param name="hasHeader">是否包含头信息</param>
        /// <returns></returns>
        public static string SerializeTo<T>(T obj, bool oneLine, bool hasHeader)
        {
            lock (locker)
            {
                using (StreamReader reader = new StreamReader(new MemoryStream(), Encoding.UTF8))
                {
                    Stream ms = reader.BaseStream;
                    SerializeInner(ms, obj!, Encoding.UTF8, oneLine, hasHeader);
                    ms.Position = 0;
                    return reader.ReadToEnd();
                }
            }
        }

        /// <summary>
        /// XML反序列化
        /// </summary>
        /// <param name="xml">需要反序列化的xml字符串</param>
        /// <returns>返回T对象</returns>
        public static T DeserializeFrom<T>(string xml) where T : class
        {
            lock (locker)
            {
                using (StringReader sr = new StringReader(xml))
                {
                    XmlSerializer formatter = new XmlSerializer(typeof(T));
                    return (T)formatter.Deserialize(sr);
                }
            }
        }

        /// <summary>
        /// XML反序列化
        /// </summary>
        /// <param name="type">数据类型</param>
        /// <param name="xml">xml数据</param>
        /// <returns></returns>
        public static object DeserializeFrom(Type type, string xml)
        {
            lock (locker)
            {
                using (StringReader sr = new StringReader(xml))
                {
                    XmlSerializer formatter = new XmlSerializer(type);
                    return formatter.Deserialize(sr);
                }
            }
        }

        /// <summary>
        /// 序列化方法
        /// </summary>
        /// <param name="stream">xml文件流</param>
        /// <param name="obj">文件对象</param>
        /// <param name="encoding">字符集</param>
        /// <param name="oneLine"></param>
        /// <param name="hasHeader"></param>
        private static void SerializeInner(Stream stream, object obj, Encoding encoding, bool oneLine = false, bool hasHeader = true)
        {
            XmlSerializer seralizer = new XmlSerializer(obj.GetType());
            XmlWriterSettings setting = new XmlWriterSettings
            {
                Indent = !oneLine,
                NewLineChars = oneLine ? "" : Environment.NewLine,
                Encoding = encoding,
                OmitXmlDeclaration = !hasHeader
            };

            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "");
            using (XmlWriter writer = XmlWriter.Create(stream, setting))
            {
                seralizer.Serialize(writer, obj, ns);
            }
        }
    }
}
